home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / pty4 / part05 < prev    next >
Encoding:
Text File  |  1992-02-18  |  56.7 KB  |  1,830 lines

  1. Newsgroups: comp.sources.unix
  2. From: brnstnd@nyu.edu (Dan Bernstein)
  3. Subject: v25i131: Generalized interface to pseudo-tty devices, Part05/09
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: brnstnd@nyu.edu (Dan Bernstein)
  8. Posting-Number: Volume 25, Issue 131
  9. Archive-Name: pty4/part05
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 5 (of 9)."
  18. # Contents:  NEW QUESTIONS ULOGS.draft1 pty.1 ptysecure.c ptysigler.c
  19. #   timer.c
  20. # Wrapped by vixie@cognition.pa.dec.com on Wed Feb 19 13:35:04 1992
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'NEW' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'NEW'\"
  24. else
  25. echo shar: Extracting \"'NEW'\" \(9164 characters\)
  26. sed "s/^X//" >'NEW' <<'END_OF_FILE'
  27. pty 4.0 (``newpty'' below) is an almost complete rewrite of pty 3.0
  28. X(``oldpty''). Some features and misfeatures have been removed:
  29. X
  30. X1. newpty no longer allows for systems without file descriptor passing.
  31. X2. newpty does not go to obscene lengths to ensure non-blocking I/O.
  32. X3. newpty does not worry about the buggy MAXUPRC handling in BSD 4.{2,3}.
  33. X4. newpty no longer maintains an entire hierarchy of special files.
  34. X5. The x* utilities and -xS are gone.
  35. X6. For security reasons, -f is gone.
  36. X7. sessuser is gone, as nobody uses it. In its place is sessmenu (see below).
  37. X8. The semi-automatic INSTALL has been replaced by a sane configuration system.
  38. X
  39. Several new features have been added:
  40. X
  41. X9. You can now disconnect, reconnect, and rename sessions remotely.
  42. X10. newpty performs much better tests than oldpty for tty security.
  43. X11. newpty uses my sigsched library; this greatly simplifies the code.
  44. X12. newpty's random tty searching is not vulnerable to secondary clustering.
  45. X13. newpty supports session and session-connection logs as well as [uw]tmp.
  46. X14. newpty now maintains a single, reliable communications file for each pty.
  47. X15. Internally, the master and signaller are much more cleanly separated.
  48. X16. oldpty's -d served two different functions. newpty's -d is simpler.
  49. X17. When oldpty reaches EOF on input, it loops forever. newpty doesn't.
  50. X18. newpty's error messages are, believe it or not, comprehensible.
  51. X19. newpty lets the user set his host field in utmp (this is configurable).
  52. X20. newpty gives the slave a PTY variable showing its extension (e.g., p7).
  53. X21. A ``sessmenu'' utility can serve as a friendly session-aware login wrapper.
  54. X22. newpty includes ``utmpinit'' to properly initialize /etc/utmp.
  55. X23. A ``nobuf'' utility now provides a completely transparent channel.
  56. X
  57. X
  58. XFurther notes on these:
  59. X
  60. X1. Large sections of code in oldpty were dedicated to handling systems
  61. without reliable file descriptor passing (i.e., many BSD 4.2 variants).
  62. In newpty this code is gone. I still work around the fd passing bugs
  63. that appear in the latest SunOS and Ultrix releases.
  64. X
  65. X2. In oldpty, I spent a lot of time on non-blocking I/O. There's a long
  66. and sad story behind this; read on.
  67. X
  68. XEven though every bit of semantics of read() and write() is drastically
  69. affected by whether the descriptor is non-blocking, UNIX implementors
  70. through the years have always---I mean *always*---provided for
  71. non-blocking I/O on a given open file, rather than on a particular
  72. descriptor. This is fine for programs which open their own descriptors.
  73. But pty has to do I/O on input and output descriptors which someone else
  74. has set up. It can't set NDELAY on those descriptors, because another
  75. innocent program might be reading or writing the same descriptors, and
  76. NDELAY will in all probability make that program go into an infinite
  77. loop!
  78. X
  79. Of course, what most people do is just select() for readability or
  80. writability, then assume the operations won't block. The problem is that
  81. they might block. By the time pty gets around to reading, someone else
  82. could have read the input which caused select() to return. Similarly,
  83. any write of N bytes to a pipe with M bytes free, with N > M > 0, will
  84. block, even though select will return true. So I figured out a way to
  85. achieve true per-process non-blocking I/O: simply arrange for SIGALRM to
  86. be sent every fraction of a second, interrupting any blocking read() or
  87. write(). This works---but not under BSD 4.2, which restarts system
  88. calls. oldpty's configuration process reflected this extra complexity.
  89. X
  90. Now I've seen the light. True non-blocking I/O be damned. I'm just going
  91. to select() and do my operations like everyone else. It's not my fault
  92. that so few implementors understand why non-blocking I/O on an entire
  93. open file, rather than a particular descriptor, is so useless. If emacs
  94. and screen and so many other programs don't care, I won't either. newpty
  95. takes none of oldpty's precautions, and if it ever blocks because a pipe
  96. is too full or because there are multiple readers, don't blame me. Blame
  97. a truly senseless OS design decision.
  98. X
  99. X3. All BSD variants have a maximum process limit, MAXUPRC or maxuprc in
  100. X<sys/param.h>. When there are MAXUPRC processes with some uid (other
  101. than 0), the next fork() by that uid will fail. A bit of thought will
  102. convince you that MAXUPRC should apply to the real uid. If it applied to
  103. the effective uid, then it would affect execve() of setuid programs.
  104. XEven worse, if you had a program setuid (say) ingres, and MAXUPRC users
  105. ran that program at once, no other users would be able to run it! So
  106. applying MAXUPRC to the effective uid is a really bad idea.
  107. X
  108. Guess what? Almost all vendors have applied MAXUPRC to the effective
  109. uid. On the other hand, few of them have paid any attention to execve()
  110. of setuid programs, so in the ingres case any number of users can run
  111. the program, dragging the process count way above MAXUPRC. If you have
  112. any experience in crashing UNIX systems you'll guess what I'm about to
  113. say: If the process count for a uid does go above MAXUPRC, then as soon
  114. as MAXUPRC of those processes die, the system will panic. (This is true
  115. under, e.g., Ultrix.) Brilliant.
  116. X
  117. As you can imagine, none of this is conducive to safe setuid
  118. programming. System panics aside, pty has to fork twice, and I didn't
  119. want to limit it to MAXUPRC/2 simultaneous invocations. So oldpty swaps
  120. its uid and euid around each fork.
  121. X
  122. This introduces its own set of problems. First, the moment a setuid
  123. program does setreuid(geteuid(),getuid()), it opens itself up to ptraces
  124. by the (original) real uid. Then all security is lost. Second, this
  125. swapping is utterly pointless on systems with a sane MAXUPRC based on
  126. real uids. Finally, I haven't seen anyone else even considering this
  127. issue, let alone implementing workarounds.
  128. X
  129. So once again I've joined the crowd. newpty pretends that process limits
  130. don't exist. If it fails miserably, or your system crashes, just because
  131. too many users run setuid programs at once, blame an OS design decision
  132. as senseless as the lack of per-descriptor non-blocking I/O.
  133. X
  134. X5. The x* utilities and -xS are gone because it was easier in practice
  135. for unprivileged users to install their own copies of pty rather than
  136. use the system copy in some weird mode.
  137. X
  138. X10. With the default settings, oldpty would detect when someone else had
  139. the tty open. (Essentially the same test appeared several months later
  140. in a ``critical'' telnetd/rlogind security patch from Sun.) There were
  141. two problems: first, in a common case, pty would end up dying instead
  142. of simply trying another tty; second, the test can be defeated without
  143. too much trouble (though after these years I'm not sure there are more
  144. than a few dozen people in the world who know how to exploit this).
  145. X
  146. pty 4.0 uses a much more reliable test---it's now more secure than the
  147. tests used by Sun, Convex, DEC, and several other vendors. See the
  148. SECURITY file for further details.
  149. X
  150. X11. Internally, pty 4.0 now uses my signal-schedule (aka non-preemptive
  151. threads) library for multitasking. This makes the master much, much
  152. easier to understand, stops certain race conditions, and removes any
  153. need for explicit variable locks (like flagqwinch in oldpty).
  154. X
  155. X12. oldpty's pseudo-random tty searching started from a random spot, but
  156. searched in a fixed order from that spot. This left it open to secondary
  157. clustering. In newpty I added (in effect) a secondary hash function, and
  158. since tertiary clustering simply doesn't happen in practice, this
  159. problem should be gone.
  160. X
  161. X13. newpty supports the session and session-connection logs described in
  162. my pty paper. I fervently hope that these replace utmp and wtmp, if only
  163. because they have enough space to store full host information!
  164. X
  165. X14. In oldpty, the master process would create a communications file
  166. only while it was disconnected, then remove the file when it was
  167. reconnected. newpty maintains the communications file all the time.
  168. This eliminates some deep races and lets the master maintain certain
  169. information instead of storing it in a file for other processes to read.
  170. X
  171. X15. The roles of the master and signaller are now much more clearly
  172. separated. The master does not know whether the signaller is detached,
  173. or is manipulating a tty, or supports job control. The master does not
  174. attempt to associate itself with the signaller's tty on reconnect.
  175. Instead it always remains associated with the slave's tty.
  176. X
  177. X16. In oldpty, pty -d (detached) combined two functions. One had to do
  178. with the (obsolete) kernel concept of a controlling tty. The other told
  179. pty that it was starting under a tty and should pay attention to that
  180. tty's modes in constructing modes for the pseudo-tty. In newpty, -d
  181. controls only the second function. pty will automatically handle the
  182. kernel's controlling ttys correctly in all situations.
  183. X
  184. X17. newpty now treats true EOF on input as (1) a disconnect, if it's a
  185. session; (2) forcing -R, so that it won't read further input, if it's
  186. not a session. This is a really tricky issue---ever since UNIX supported
  187. ttys it's had two different concepts of EOF, and every program that
  188. handles ttys has to deal with both. If you have any better suggestions,
  189. let me know.
  190. END_OF_FILE
  191. if test 9164 -ne `wc -c <'NEW'`; then
  192.     echo shar: \"'NEW'\" unpacked with wrong size!
  193. fi
  194. # end of 'NEW'
  195. fi
  196. if test -f 'QUESTIONS' -a "${1}" != "-c" ; then 
  197.   echo shar: Will not clobber existing file \"'QUESTIONS'\"
  198. else
  199. echo shar: Extracting \"'QUESTIONS'\" \(8173 characters\)
  200. sed "s/^X//" >'QUESTIONS' <<'END_OF_FILE'
  201. I selected the questions below from articles posted to the USENET
  202. newsgroup comp.unix.questions. All the questions are reasonably easy to
  203. answer with pty and not too easy to answer with other widely available
  204. tools. So I hope this file is useful, and I hope other software authors
  205. adopt the QUESTIONS idea.
  206. X
  207. X
  208. X
  209. X1. How do I redirect telnet's input?
  210. X
  211. Answer: Run pty telnet instead of telnet.
  212. X
  213. Long answer: Be careful---as soon as telnet successfully connects to the
  214. remote host, it flushes its input. So if you do something like
  215. X
  216. X   (echo help; echo quit) | pty telnet whatever 25
  217. X
  218. telnet will read ``help'' and ``quit'', then after a second or two
  219. connect to the host, then flush the input. It won't get any input after
  220. that. An easy solution is to delay the input a few seconds:
  221. X
  222. X   (sleep 5; echo help; echo quit) | pty telnet whatever 25
  223. X
  224. X(Try it!) For a more robust solution, see question #4 below.
  225. X
  226. Why this was a problem: UNIX beginners learn that they can feed input to
  227. programs with redirection or with pipes. They're often surprised to see
  228. that this doesn't work:
  229. X
  230. X   (echo help; echo quit) | telnet whatever 25
  231. X
  232. telnet, like many other utilities, considers itself an ``interactive''
  233. program. It assumes that it's talking to a user's terminal directly, so
  234. that it can put the terminal into character-at-a-time mode. If you
  235. redirect its input, it won't be able to find the terminal, and it'll
  236. complain or do weird things. Similar comments apply to editors, like vi.
  237. X
  238. Why the answer solves the problem: pty creates a pseudo-terminal---
  239. something which looks like a normal terminal but is under pty's control.
  240. pty runs telnet under the pseudo-terminal and forwards all input and
  241. output. Now telnet works, because it sees the pseudo-terminal and thinks
  242. it's talking to the user.
  243. X
  244. X
  245. X
  246. X2. How do I redirect passwd's input?
  247. X
  248. Answer: pty passwd.
  249. X
  250. Long answer: Like telnet, passwd flushes its input, so you probably want
  251. to do something like
  252. X
  253. X   (sleep 5; echo oldpassword; sleep 5; echo newpassword; \
  254. X     sleep 5; echo newpassword) | pty passwd
  255. X
  256. A better solution appears in #4 below. If you're using passwd as a
  257. system administrator to change somebody else's password, you want
  258. X
  259. X   (sleep 5; echo password; sleep 5; echo password) | pty passwd shmoe
  260. X
  261. I certainly don't recommend keeping passwords visible in shell scripts,
  262. but if you ever need to do this, you can. In single-user mode this is
  263. reasonably safe.
  264. X
  265. Why this was a problem: passwd is even more insistent than telnet on
  266. talking to a user directly. So it opens /dev/tty for its input. Some
  267. other programs open /dev/tty for both input and output. This avoids any
  268. redirection you might have put into place.
  269. X
  270. Why the answer solves the problem: As before, pty creates a
  271. pseudo-terminal for passwd to talk to. When passwd opens /dev/tty, it
  272. gets the pseudo-terminal. All the redirection is outside pty, safe from
  273. passwd's tampering.
  274. X
  275. X
  276. X
  277. X3. Why doesn't tr '[A-Z]' '[a-z]' | cat -t print anything I type?
  278. X
  279. Answer: Do  nobuf tr '[A-Z]' '[a-z]' | cat -t.
  280. X
  281. Long answer: You can use pty -0 (also known as condom, or, if you're not
  282. in such a picturesque mood, as ttyprotect) in place of nobuf, but nobuf
  283. invokes pty carefully so that EOF on the input will be passed through to
  284. tr. The effect of this pipeline is to change all uppercase letters into
  285. lowercase, then print control characters visibly (e.g., ^H instead of
  286. backspace).
  287. X
  288. Why this was a problem: The original pipeline, without nobuf, doesn't
  289. seem to produce any output. What happens is that tr says ``Oh, I say.
  290. My output is going into a pipe! I guess there's no point in sending
  291. output as fast as possible. So I'll build up a buffer of output, say up
  292. to 8192 characters, before I send any of it.'' In fact, any program
  293. which uses stdio will do the same thing. The problem is that the guess
  294. is wrong---you don't want 8192 characters held inside tr. You want each
  295. line sent through without any buffering.
  296. X
  297. Why the answer solves the problem: Once again, nobuf (really pty)
  298. creates a pseudo-terminal and runs tr inside it. Then tr (really stdio)
  299. says ``Oh, I say. My output is going into a terminal! I'll bet there's a
  300. user watching every word. So I'll send each line as soon as I'm done
  301. with it.'' And it does.
  302. X
  303. X
  304. X
  305. X4. How do I start a program, respond to its prompts, give the correct
  306. X   replies, and catch the output---all from a script?
  307. X
  308. If you're good at shell programming, you might have already figured out
  309. most of this. Let's say you're on a machine (like a Sun) which can
  310. create named pipes with mknod foo p. You might try to automate a telnet
  311. session, keeping a record in a file, like this:
  312. X
  313. X  #!/bin/sh
  314. X  (umask 077; mknod input p; mknod output p)
  315. X  telnet foo < input | tee record > output &
  316. X  exec 4>input 5<output
  317. X  waitfor login: <&5 2>/dev/null
  318. X  echo 'username' >&4
  319. X  waitfor Password: <&5 2>/dev/null
  320. X  echo 'password' >&4
  321. X  # etc.
  322. X
  323. X(waitfor reads its input, character by character, until its first
  324. argument matches the most recent characters as a literal string.)
  325. X
  326. This almost works. sh can handle this sort of automation without
  327. trouble. The only problem is that telnet is interactive. That's where
  328. pty comes in. This works:
  329. X
  330. X  #!/bin/sh
  331. X  (umask 077; mknod input p; mknod output p)
  332. X  pty -0 telnet foo < input | nobuf tee record > output &
  333. X  exec 4>input 5<output
  334. X  waitfor login: <&5 2>/dev/null
  335. X  echo 'username' >&4
  336. X  waitfor Password: <&5 2>/dev/null
  337. X  echo 'password' >&4
  338. X  # etc.
  339. X
  340. On machines without named pipes, you'll have to create (unnamed) pipes
  341. in C. But whatever method of automation you use, pty will let you apply
  342. that method to an interactive program.
  343. X
  344. X
  345. X
  346. X5. How do I get rn to process KILL files in the background?
  347. X
  348. Answer:
  349. X
  350. X   % sess -R sh -c 'sessname; rn' &
  351. X   [1] 20417        < the shell prints this >
  352. X   session pf       < this comes from sessname >
  353. X   < rn produces output in the background, and you can do something else. >
  354. X   < ... >
  355. X   < when you want to put it back into the foreground: >
  356. X   % %1
  357. X   sess -R sh -c 'sessname; rn'         < the shell prints this >
  358. X   ^C
  359. X   % sess reconnect pf
  360. X   < now it's just as if rn had started in the foreground. >
  361. X
  362. sess is an abbreviation for ``pty -s''. It starts a _session_ which you
  363. can disconnect and reconnect. Later on, when you put it into the
  364. background and type ^C, it stays disconnected in the background. Then
  365. you can start a new session and reconnect to it.
  366. X
  367. X-R means ``don't read.'' It's a lot like rsh -n. It tells pty not to
  368. read anything from the keyboard. When you start ``sess reconnect pf'',
  369. you don't specify -R, so pty does take keyboard input.
  370. X
  371. Why this was a problem: rn is yet another ``interactive'' program. It
  372. insists on being in the foreground before it does anything else.
  373. Unfortunately, after you put it into the foreground, it might spend ten
  374. minutes processing KILL files!
  375. X
  376. Why the answer solves the problem: Once again, pty creates a
  377. pseudo-terminal for rn. Under that pseudo-terminal, rn is in the
  378. foreground, so it will happily process KILL files.
  379. X
  380. X
  381. X6. How do I get the terminal speed from a shell script?
  382. X
  383. Answer: Under sh, speed="`pty stty speed`".
  384. X
  385. Why this was a problem: When you type ``stty speed'', most versions of
  386. stty will look for the terminal on stdout, get the speed of that
  387. terminal, and print the result on stderr. So if you try
  388. X
  389. X   speed="`stty speed`"
  390. X
  391. from sh, two things go wrong. The first is that sh is reading stty's
  392. output, so when stty looks for the terminal, it'll find only a pipe. The
  393. second is that when stty prints the result, it goes to stderr, which sh
  394. isn't paying attention to.
  395. X
  396. Other versions of stty look for the terminal on stdin or stderr, and
  397. print the result to stdout. So even if you kludge around the first
  398. problem by moving file descriptors around, chances are your code will
  399. mysteriously blow up on the next machine.
  400. X
  401. Why the answer solves the problem: pty creates a pseudo-terminal for
  402. stty, with exactly the same characteristics (including speed) as the
  403. original tty. It doesn't matter whether stty looks at stdin or stdout or
  404. stderr. It'll just see the pseudo-tty. It also doesn't matter whether
  405. stty prints its result to stdout or stderr. pty will collect both
  406. results into its stdout for the shell.
  407. END_OF_FILE
  408. if test 8173 -ne `wc -c <'QUESTIONS'`; then
  409.     echo shar: \"'QUESTIONS'\" unpacked with wrong size!
  410. fi
  411. # end of 'QUESTIONS'
  412. fi
  413. if test -f 'ULOGS.draft1' -a "${1}" != "-c" ; then 
  414.   echo shar: Will not clobber existing file \"'ULOGS.draft1'\"
  415. else
  416. echo shar: Extracting \"'ULOGS.draft1'\" \(6271 characters\)
  417. sed "s/^X//" >'ULOGS.draft1' <<'END_OF_FILE'
  418. An analysis of user login records
  419. Daniel J. Bernstein
  420. draft 1
  421. X10/5/91
  422. X
  423. X
  424. X1. BSD login records
  425. X
  426. On a BSD UNIX system, three files keep track of user logins. /etc/utmp
  427. records which users are on which lines, and when they logged in (or, for
  428. unused lines, logged out). To put it differently, utmp records the most
  429. recent start or stop time of the latest session on each line, and which
  430. user owns or owned that session. utmp also records the first 16
  431. characters of the remote hostname for any network connection.
  432. X
  433. A parallel file /usr/adm/wtmp records all changes to utmp. The last wtmp
  434. entry for line xx is the current utmp entry for line xx. wtmp also uses
  435. special codes to indicate reboots and other unusual events. Typically
  436. wtmp is ``rotated'' every month: /usr/adm/wtmp is cleared, with a copy
  437. of the old information saved in /usr/adm/wtmp.0. Any previous wtmp.0 is
  438. saved in wtmp.1, and so on up to wtmp.7, which is thrown away.
  439. X
  440. The third file is /usr/adm/lastlog, a flat database indexed by uid
  441. showing the most recent login entry for each user. lastlog and utmp have
  442. different formats but carry essentially the same information.
  443. X
  444. X
  445. X2. Problems
  446. X
  447. utmp provides only 16 characters for hostnames which are often much too
  448. long to fit. Once utmp (and wtmp and lastlog) truncate a name, any extra
  449. characters are lost permanently. Other information---the remote TCP
  450. port, the remote user as seen by rlogind or via RFC 931, etc.---isn't
  451. recorded at all. This makes it very difficult to trace network attacks.
  452. X
  453. XFurthermore, when users take advantage of session management (as
  454. described in []), utmp and wtmp lose even more information. A user may
  455. start a session at work, disconnect it without logging out, go home, and
  456. reconnect to the session from an entirely different location. It makes
  457. sense to talk about the start and stop time of the session, each connect
  458. and disconnect time, and each connect location. utmp and wtmp record
  459. none of this.
  460. X
  461. Another problem with utmp is that it has never been clear whether utmp
  462. should record all connections (see []) or only interactive connections.
  463. Given that the programs which depend most heavily on utmp---to wit, user
  464. communication programs such as ``talk'' and ``write''---should only see
  465. interactive connections, it makes sense to omit windows and subshells,
  466. but then window information is lost.
  467. X
  468. lastlog is mainly an optimization: login and finger both report the last
  469. login time for a particular user, and it would be wasteful to search
  470. through wtmp each time. But lastlog only keeps track of the very latest
  471. login, with no indication of any previous logins. It does not record
  472. logout time. Even worse, users and administrators cannot find out when
  473. their accounts are being attacked, because lastlog only records
  474. successful logins.
  475. X
  476. X
  477. X3. Solutions
  478. X
  479. One ``solution'' is to add more and more fields to utmp, recording more
  480. and more information. But this is the wrong strategy. Consider the
  481. parallel wtmp file. If utmp has (for instance) fields for the latest
  482. connection, then every time a user connects or disconnects, the basic
  483. session fields will be repeated in wtmp for no good reason. This wastes
  484. disk space but also indicates a fundamental failure in the model.
  485. X
  486. The right solution is to give each file a specific purpose. utmp should
  487. only keep track of sessions; the host field should be removed. Complete
  488. connection information, including connect and disconnect times, remote
  489. host as both IP address and name, remote port, and remote user if known,
  490. can go into a separate file.
  491. X
  492. To solve the problem of interactive versus non-interactive sessions,
  493. utmp should be split in two. The original utmp should be preserved for
  494. interactive, user-to-user communication. A separate file should record
  495. all sessions.
  496. X
  497. lastlog can be improved in many ways, which we will not discuss in
  498. detail here. Various vendors (e.g., DEC) have already added features
  499. along these lines.
  500. X
  501. X
  502. X4. pty user login files
  503. X
  504. The author's pseudo-tty session manager, pty 4.0 ([]), maintains several
  505. user login files. utmp and wtmp record interactive sessions as above.
  506. The user can specify at session startup whether the session is
  507. interactive or not. For maximum flexibility, pty lets the user choose
  508. his host field in utmp. This way the user can configure his sessions to
  509. work properly with various utmp-processing programs. (The system
  510. administrator may disable these features.)
  511. X
  512. All disconnectable sessions are recorded in sessnow, which has fields
  513. for username, uid, start/stop time, session master process id, and line.
  514. sesslog keeps a permanent record of changes to sessnow. Lines are
  515. specified by two characters (e.g., p0 for /dev/ttyp0).
  516. X
  517. The relation between sessions and connections is recorded in scnow.
  518. scnow lists each session by line, the latest connection start/stop time,
  519. and complete remote host information. All changes to scnow are listed in
  520. sclog.
  521. X
  522. Note that it makes sense to record connections separately, even those
  523. not connected to any particular session. The connection managers (getty,
  524. telnetd, etc.) might keep connnow and connlog files listing current
  525. connections. In the meantime pty maintains sessnow, sesslog, scnow,
  526. sclog, utmp, and wtmp. This finally makes a logical set of records for
  527. which user was using what session from where.
  528. X
  529. Notice the clean distinction between connection information and session
  530. information. All session information is maintained by one program, pty.
  531. The utmp handling can be completely removed from init, getty, telnetd,
  532. login, rlogind, screen, xterm, sunview, and dozens of other programs.
  533. X
  534. X
  535. X5. Security issues
  536. X
  537. It is worth noting that Sun destroyed any credibility it might have had
  538. in its user login files by making /etc/utmp mode 666. This is what the
  539. author calls a SCINUP: Security Compromise Introduced in the Name of
  540. User Power. Sun found so many programs in its toolset that wanted to
  541. update utmp that it removed all utmp protection rather than implement a
  542. proper security mechanism. Years later, security experts (and system
  543. crackers) are still finding devastating holes caused by Sun's incredibly
  544. poor judgment.
  545. X
  546. Needless to say, the author does not approve of unprotected login files.
  547. A single program---pty---can provide safe utmp service for all programs
  548. which need it.
  549. X
  550. X
  551. References
  552. X
  553. XXXX
  554. END_OF_FILE
  555. if test 6271 -ne `wc -c <'ULOGS.draft1'`; then
  556.     echo shar: \"'ULOGS.draft1'\" unpacked with wrong size!
  557. fi
  558. # end of 'ULOGS.draft1'
  559. fi
  560. if test -f 'pty.1' -a "${1}" != "-c" ; then 
  561.   echo shar: Will not clobber existing file \"'pty.1'\"
  562. else
  563. echo shar: Extracting \"'pty.1'\" \(7074 characters\)
  564. sed "s/^X//" >'pty.1' <<'END_OF_FILE'
  565. X.TH pty 1
  566. X.SH NAME
  567. pty \- run a program under a pseudo-terminal session
  568. X.SH SYNTAX
  569. pty
  570. X[
  571. X\fB\-qQve3EdDjJsStTrR0\fI\fP
  572. X] [
  573. X\fB\-h\fIhost\fP
  574. X] [
  575. X\fB\-O\fIremote\fP
  576. X] [
  577. X\fB\-p[cCdDeEnNrRsS780]\fI\fP
  578. X] [
  579. X\fB\-x[cCeEfFrRsSuUwWxX]\fI\fP
  580. X] [
  581. X\fB\-ACHUVW\fI\fP
  582. X]
  583. program
  584. X[
  585. arg ...
  586. X]
  587. X.SH DESCRIPTION
  588. X.B pty
  589. detaches itself from its original
  590. terminal,
  591. allocates a new pseudo-terminal session,
  592. and runs
  593. X.I program
  594. on that pseudo-terminal
  595. with any given arguments.
  596. X.B pty
  597. lets you easily disconnect from and reconnect to
  598. sessions;
  599. it has over fifty options for precise control,
  600. and is meant to act as the sole interface
  601. between pseudo-terminals and the rest of the system.
  602. X
  603. X.B pty
  604. transmits I/O and job control transparently,
  605. so that
  606. X(for instance)
  607. X.B pty vi
  608. appears to the user to be the same as
  609. X.B vi,
  610. but in fact
  611. X.B pty
  612. is much more flexible.
  613. X.B pty vi
  614. can be redirected, for example,
  615. while a simple
  616. X.B vi
  617. cannot.
  618. X.B pty
  619. has several other uses, as described below.
  620. X
  621. There are a few very common invocations of
  622. X.B pty.
  623. The most common is just
  624. X.B pty \fIprogram,
  625. with no options.
  626. As described in the
  627. X.B pty-basic
  628. man page,
  629. this can be redirected or placed inside a pipeline,
  630. and
  631. X.I program
  632. will behave as if it hadn't been redirected at all.
  633. X.B pty \-s \fIprogram
  634. sets up a disconnectable session,
  635. as described further in
  636. the
  637. X.B sess(1)
  638. man page.
  639. A disconnectable session will survive modem hangups,
  640. network glitches, etc. It also
  641. lets the user disconnect his session at work
  642. and reconnect later at home without killing
  643. his jobs in progress.
  644. X.B pty \-0 \fIprogram
  645. isolates the rest of the world from
  646. X.I program
  647. in several ways;
  648. it is described further in
  649. the
  650. X.B condom(1)
  651. man page.
  652. X
  653. The two next most commonly used options
  654. are 
  655. X.B\-d,
  656. if
  657. X.B pty
  658. is started without a controlling terminal;
  659. and
  660. X.B\-xu,
  661. which makes an entry in
  662. X/etc/utmp.
  663. X.B pty \-d
  664. is especially useful inside
  665. X.B rsh,
  666. where interactive programs normally fail for lack of a tty.
  667. X
  668. As noted above,
  669. some programs (such as
  670. X.B vi)
  671. don't like taking input or output
  672. from a pipe. Under
  673. X.B pty,
  674. they won't notice a thing.
  675. Other programs,
  676. based on the standard I/O library,
  677. buffer as much output as possible
  678. if they're in a pipe.
  679. Under
  680. X.B pty,
  681. they'll buffer only a line of output as usual.
  682. X.B pty
  683. is very careful to restore terminal modes upon
  684. stopping or exiting;
  685. a careless
  686. X.I program
  687. is shielded from your terminal.
  688. Otherwise,
  689. X.B pty \fIprogram
  690. X``feels'' just like
  691. X.I program.
  692. X
  693. X.B pty
  694. sets file descriptor 0 to input from the
  695. pseudo-terminal, 1 and 2 to output.
  696. X.B pty
  697. also supports the ``3 is /dev/tty'' convention:
  698. it sets up file descriptor 3 for input from, output to,
  699. and terminal commands for
  700. X/dev/tty
  701. X(not the original tty, but the pseudo tty).
  702. X
  703. X.B pty
  704. has many options,
  705. as documented in the
  706. X.B pty-opts
  707. man page.
  708. X.SH DIAGNOSTICS
  709. X.TP
  710. various usage messages
  711. XExit 1.
  712. X.TP
  713. X.I cannot find controlling tty; try -d?
  714. X.B pty
  715. is unable to find its current terminal to copy tty modes from.
  716. X(This version of
  717. X.B pty
  718. defines its controlling tty to be the first one of
  719. the following which is a tty device:
  720. descriptor 3, /dev/tty, descriptor 0, descriptor 1, descriptor 2.)
  721. You probably want to use
  722. X.B\-d
  723. to tell
  724. X.B pty
  725. to make a pseudo-terminal from scratch.
  726. XExit 2.
  727. X.TP
  728. X.I cannot get modes of original tty
  729. This shouldn't happen.
  730. XExit 3.
  731. X.TP
  732. X.I cannot set modes of original tty
  733. This shouldn't happen.
  734. XExit 4.
  735. X.TP
  736. X.I no ptys available
  737. Self-explanatory.
  738. XExit 5.
  739. X.TP
  740. X.I cannot set up open descriptors
  741. There is a serious problem with your pseudo-terminal setup.
  742. Report this error to your system administrator.
  743. XExit 6.
  744. X.TP
  745. X.I cannot fork
  746. X.I pty
  747. has run out of processes.
  748. XExit 7.
  749. X.TP
  750. X.I signaller cannot change to session directory
  751. Probably the session directory has not been created with
  752. the appropriate modes, or
  753. X.B pty
  754. is not running with sufficient privilege.
  755. Note that this error is often duplicated or triplicated,
  756. as the various pieces of
  757. X.B pty
  758. depend on the session directory for communication,
  759. including communication of this error!
  760. XExit 8.
  761. X.TP
  762. X.I out of memory
  763. Self-explanatory.
  764. XExit 9.
  765. X.TP
  766. X.I cannot create internal pipe
  767. X.TP
  768. X.I cannot setsid
  769. These errors will not happen in my lifetime.
  770. If they do, it's probably because of quantum bit flips.
  771. XExit 10.
  772. X.TP
  773. X.I signaller having trouble
  774. X.TP
  775. X.I signaller cannot read success code from master
  776. Something has gone very wrong with
  777. the communications paths inside
  778. X.B pty.
  779. This should not happen.
  780. XExit 11.
  781. X.TP
  782. X.I session already connected somewhere else
  783. This error can happen upon session reconnect
  784. and, in that case, is self-explanatory.
  785. XExit 16.
  786. X.TP
  787. X.I cannot find session; does it exist?
  788. Self-explanatory, again upon session reconnect.
  789. XExit 17.
  790. X.TP
  791. various \fIcannot\fR messages
  792. If something is slightly wrong with the user-supplied environment
  793. X(for instance,
  794. if
  795. X.I program
  796. does not exist)
  797. X.B pty
  798. will print an error but
  799. will exit 0,
  800. as if it had successfully executed a shell script
  801. which then detected the error.
  802. This is consistent with the way that
  803. X.B pty
  804. does not report the exit status of
  805. X.I program.
  806. X.TP
  807. X.I warning: cannot write utmp entry
  808. Self-explanatory.
  809. X.TP
  810. X.I warning: cannot dissociate from current tty
  811. X.B pty
  812. is unable to submit to the kernel's controlling tty demands.
  813. If, for example, the tty has
  814. TIOCEXCL mode set, then there is absolutely no reliable
  815. way for a program to dissociate itself from that tty.
  816. This may or may not affect
  817. X.I program.
  818. X.TP
  819. X.I warning: no secure ptys available
  820. X.B pty
  821. X4.0
  822. comes with security features which,
  823. on a standard BSD 4.3-derived system,
  824. guarantee that any
  825. pseudo-tty
  826. used will be completely clean.
  827. This message means that no clean ptys are available.
  828. X(There is an option which will force
  829. X.B pty
  830. to exit in this case.)
  831. X.TP
  832. X.I warning: cannot set exclusive use on pseudo-tty
  833. Self-explanatory.
  834. X.SH BUGS
  835. None known, but they're probably hiding somewhere.
  836. X.SH "MANDATORY SERMON"
  837. UNIX programs have traditionally allocated pseudo-terminals
  838. with the same code copied from one application to the next.
  839. Maintaining or adding features to the pseudo-terminal system
  840. is nearly impossible,
  841. as so many programs depend on its current internals.
  842. Porting a program to a new system often means
  843. rewriting its pseudo-terminal allocation code.
  844. And the reliability and security of the entire system are
  845. jeapordized by
  846. this decentralized management
  847. of one of the UNIX system's most important resources.
  848. X
  849. X.B pty
  850. solves these problems.
  851. If programmers use
  852. X.B pty
  853. instead of writing equivalent code in each program,
  854. then everything becomes much more portable and bug-free.
  855. The only program that need be changed for a different
  856. system with a different pseudo-terminal mechanism
  857. is
  858. X.B pty.
  859. X(This is called ``modularity,''
  860. X``interface design,''
  861. or ``outside-in programming.'')
  862. X.B pty
  863. is small enough to be reasonbly reliable,
  864. and it solves the security problems that plague
  865. BSD pseudo-ttys.
  866. It is the author's opinion that
  867. X.B pty
  868. is the ``right'' interface between pseudo-ttys
  869. and the rest of the system.
  870. X.SH VERSION
  871. pty version 4.0, 2/9/92.
  872. X.SH AUTHOR
  873. Copyright 1992, Daniel J. Bernstein.
  874. X.SH "SEE ALSO"
  875. pty-basic(1),
  876. pty-opts(1),
  877. sess(1),
  878. condom(1),
  879. pty(4),
  880. tty(4)
  881. END_OF_FILE
  882. if test 7074 -ne `wc -c <'pty.1'`; then
  883.     echo shar: \"'pty.1'\" unpacked with wrong size!
  884. fi
  885. # end of 'pty.1'
  886. fi
  887. if test -f 'ptysecure.c' -a "${1}" != "-c" ; then 
  888.   echo shar: Will not clobber existing file \"'ptysecure.c'\"
  889. else
  890. echo shar: Extracting \"'ptysecure.c'\" \(6764 characters\)
  891. sed "s/^X//" >'ptysecure.c' <<'END_OF_FILE'
  892. X#include <sys/types.h>
  893. X#include <sys/stat.h>
  894. X#include <sys/file.h>
  895. X#include <errno.h>
  896. extern int errno;
  897. X#include "ptysecure.h"
  898. X#include "ptytty.h"
  899. X#include "config/ptymodes.h"
  900. X#include "config/ptygroup.h"
  901. X
  902. static struct stat storig;
  903. X
  904. X/* must close fdm and fds upon -1 return */
  905. int ptysecure(fdm,fds,ext,fnm,fns,flagxchown,allowinsecure)
  906. int *fdm; /* master */
  907. int *fds; /* slave */
  908. char *ext; /* char array, not string */
  909. char *fnm;
  910. char *fns;
  911. int flagxchown;
  912. int allowinsecure;
  913. X{
  914. X/* XXX: check that the pathnames are secure? nah */
  915. X/* XXX: vhangup(): don't make me laugh */
  916. X/* XXX: revoke() under 4.4 */
  917. X/* XXX: ofiles... fstat... pff... */
  918. X/* XXX: opencount()---but what about unprivileged users? */
  919. X
  920. X/* We have the master and slave open. */
  921. X/* Any number of other processes may have the slave open. */
  922. X/* Any number of other processes may have our pgrp. */
  923. X
  924. X/* We depend on never passing the master side to another process. */
  925. X/* We depend on opens of the master side being mutually exclusive. */
  926. X/* We opened the master, so no other process has the master open. */
  927. X/* Through this routine we will never close the master. */
  928. X/* Hence no other process will have the master open while we work. */
  929. X
  930. X char buf[20];
  931. X int fdp[2];
  932. X char psarg1[20];
  933. X int kidpid;
  934. X int numpsrets;
  935. X int pspid;
  936. X int flagchmodworked;
  937. X int parsingpid;
  938. X char *(psargs[3]);
  939. X int r;
  940. X
  941. X if (fstat(*fds,&storig) == -1)
  942. X   goto death;
  943. X flagchmodworked = 1;
  944. X if (storig.st_uid != geteuid())
  945. X   flagchmodworked = 0;
  946. X if (!allowinsecure && !flagchmodworked)
  947. X   goto death;
  948. X if (fchmod(*fds,0600) == -1)
  949. X  {
  950. X   flagchmodworked = 0;
  951. X   /* XXX: warning? other action? */
  952. X  }
  953. X if (!allowinsecure && !flagchmodworked)
  954. X   goto pdeath;
  955. X/* All security guarantees are off unless the slave tty is now */
  956. X/* protected from open() by normal users. In other words, we require */
  957. X/* either (1) being the owner of the tty, so that the fchmod worked, */
  958. X/* or (2) being privileged and having the slave ttys be protected */
  959. X/* all the time anyway. (2) is a better situation. (1) is more */
  960. X/* realistic for pty installations under current systems. Anyway, */
  961. X/* we don't do the more powerful security tests if we can't chmod. */
  962. X
  963. X/* Anyway, we depend on the slave tty now being unopenable by users. */
  964. X/* This situation will persist until we change the mode again. */
  965. X/* (In situation (2) it will always be true.) */
  966. X
  967. X/* We depend on read() of the master side returning 0 or -1/EIO if */
  968. X/* nobody has the slave side open, and something else otherwise. */
  969. X close(*fds); r = read(*fdm,buf,1);
  970. X if (r > 0) { /* XXX: warning? */ goto pmdeath; }
  971. X if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
  972. X  { /* XXX: warning? */ goto pmdeath; }
  973. X *fds = open(fns,O_RDWR);
  974. X if (*fds == -1) goto pmdeath;
  975. X
  976. X/*
  977. Now nobody but us has the slave side open.
  978. XFurthermore, as noted above, nobody but us has the master side open,
  979. and nobody will be able to open either the master or the slave.
  980. X
  981. But we're still not done! A process can access a tty even if the tty
  982. is completely protected, and even if it doesn't have a descriptor open
  983. to the tty. (Does this sound stupid? It is.) All it has to do is open
  984. X/dev/tty, provided that it has the right controlling terminal. Before
  985. pronouncing the tty secure we have to take care of processes with the
  986. same ctty.
  987. X
  988. We depend on the fact that all methods of associating a process with a
  989. tty depend on (1) having the actual tty (not just /dev/tty) open; or
  990. X(2) opening the tty. We know that nobody can have the actual tty open
  991. from here on.
  992. X*/
  993. X
  994. X if (flagchmodworked)
  995. X  {
  996. X   if (pipe(fdp) == -1)
  997. X     goto pdeath;
  998. X  
  999. X   switch(kidpid = fork())
  1000. X    {
  1001. X     case -1:
  1002. X       close(fdp[0]);
  1003. X       close(fdp[1]);
  1004. X       goto pdeath;
  1005. X     case 0:
  1006. X       /* XXX: WARNING! We invoke /bin/ps with our privileges! */
  1007. X       /* If we switched back to the real uid, we couldn't trust the results. */
  1008. X       close(*fdm); close(*fds);
  1009. X       close(0); if (dup(fdp[0]) != 0) exit(1);
  1010. X       close(1); if (dup(fdp[1]) != 1) exit(1);
  1011. X       close(2); if (dup(fdp[1]) != 2) exit(1);
  1012. X       close(fdp[0]);
  1013. X       close(fdp[1]);
  1014. X       close(0);
  1015. X       psargs[0] = "/bin/ps";
  1016. X       psargs[1] = psarg1;
  1017. X       psarg1[0] = 'c'; psarg1[1] = 'g'; psarg1[2] = 'a'; psarg1[3] = 'x';
  1018. X       psarg1[4] = 't'; psarg1[5] = ext[0]; psarg1[6] = ext[1]; psarg1[7] = 0;
  1019. X       psargs[2] = (char *) 0;
  1020. X       setreuid(getuid(),getuid()); /* XXX: do we really want this? */
  1021. X       execve(psargs[0],psargs,psargs + 2);
  1022. X       exit(1);
  1023. X     default:
  1024. X       close(fdp[1]);
  1025. X    }
  1026. X  
  1027. X   numpsrets = 0;
  1028. X   parsingpid = 0;
  1029. X   while (read(fdp[0],buf,1) == 1)
  1030. X    {
  1031. X     if (parsingpid)
  1032. X       if ((buf[0] != ' ') && (10 > (unsigned long) (unsigned char) (buf[0] - '0')))
  1033. X         pspid = pspid * 10 + (buf[0] - '0');
  1034. X       else if (pspid)
  1035. X        {
  1036. X         parsingpid = 0;
  1037. X     /* XXX: tell user about pspid? */
  1038. X         if ((pspid == getpid()) || (pspid == kidpid))
  1039. X           --numpsrets; /*XXX*/
  1040. X        }
  1041. X     if (buf[0] == '\n')
  1042. X      {
  1043. X       parsingpid = 1;
  1044. X       pspid = 0;
  1045. X       ++numpsrets;
  1046. X      }
  1047. X    }
  1048. X   close(fdp[0]);
  1049. X   wait((int *) 0); /*XXX*/
  1050. X
  1051. X   if (numpsrets != 1)
  1052. X    {
  1053. X     /* XXX: warning? */
  1054. X     goto pdeath;
  1055. X    }
  1056. X  }
  1057. X
  1058. X/*
  1059. We depend on /bin/ps being set up so that numpsrets == 1 implies that
  1060. there were, at some point in time, no processes (except possibly us)
  1061. with that tty as controlling tty.
  1062. X
  1063. Now lots of processes could have /dev/tty open and somehow pointing to
  1064. this tty, but there are none (other than us) with the actual tty open,
  1065. or with the master open, or with this controlling tty. There's an easy
  1066. way to finish off: we simply repeat the first test!
  1067. X*/
  1068. X close(*fds); r = read(*fdm,buf,1);
  1069. X if (r > 0) { /* XXX: warning? */ goto pmdeath; }
  1070. X if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
  1071. X  { /* XXX: warning? */ goto pmdeath; }
  1072. X *fds = open(fns,O_RDWR);
  1073. X if (*fds == -1) goto pmdeath;
  1074. X
  1075. X/*
  1076. XFinally! The pseudo-tty is secure---or at least a hell of a lot more
  1077. secure than the ttys you get from any other program.
  1078. X*/
  1079. X
  1080. X if (fchmod(*fds,PTYMODE_USED) == -1)
  1081. X  {
  1082. X   /* XXX: warning? */
  1083. X   ;
  1084. X  }
  1085. X if (flagxchown)
  1086. X  {
  1087. X   if (fchown(*fds,getuid(),PTYGROUP) == -1)
  1088. X    {
  1089. X     /* XXX: warning? */
  1090. X     ;
  1091. X    }
  1092. X  }
  1093. X return 0;
  1094. X
  1095. pmdeath:
  1096. X *fds = open(fns,O_RDWR);
  1097. pdeath:
  1098. X if (*fds != -1)
  1099. X   fchmod(*fds,PTYMODE_UNUSED);
  1100. death:
  1101. X close(*fds); /* *fds could be -1; that's okay */
  1102. mdeath:
  1103. X *fds = open("/dev/tty",O_RDWR);
  1104. X if (*fds != -1)
  1105. X  {
  1106. X   if (tty_dissoc(*fds) == -1)
  1107. X     ;
  1108. X   close(*fds);
  1109. X  }
  1110. X close(*fdm);
  1111. X return -1;
  1112. X}
  1113. X
  1114. int ptyunsecure(fdm,fds,ext)
  1115. int fdm;
  1116. int fds;
  1117. char *ext;
  1118. X{
  1119. X if (fchmod(fds,PTYMODE_UNUSED) == -1)
  1120. X  {
  1121. X   /* XXX: warning? */
  1122. X   ;
  1123. X  }
  1124. X if (getuid() != storig.st_uid)
  1125. X   if (fchown(fds,storig.st_uid,PTYGROUP) == -1)
  1126. X    {
  1127. X     /* XXX: warning? */
  1128. X     ;
  1129. X    }
  1130. X return 0;
  1131. X}
  1132. END_OF_FILE
  1133. if test 6764 -ne `wc -c <'ptysecure.c'`; then
  1134.     echo shar: \"'ptysecure.c'\" unpacked with wrong size!
  1135. fi
  1136. # end of 'ptysecure.c'
  1137. fi
  1138. if test -f 'ptysigler.c' -a "${1}" != "-c" ; then 
  1139.   echo shar: Will not clobber existing file \"'ptysigler.c'\"
  1140. else
  1141. echo shar: Extracting \"'ptysigler.c'\" \(7958 characters\)
  1142. sed "s/^X//" >'ptysigler.c' <<'END_OF_FILE'
  1143. X#include <sys/types.h>
  1144. X#include <sys/wait.h>
  1145. X#include <sys/time.h>
  1146. X#include <sys/resource.h>
  1147. X#include <signal.h>
  1148. X#include "sigsched.h"
  1149. X#include "sigdfl.h"
  1150. X#include "fmt.h"
  1151. X#include "sessconnlog.h"
  1152. X#include "ptytty.h"
  1153. X#include "ptycomm.h"
  1154. X#include "ptymisc.h"
  1155. X#include "ptyerr.h"
  1156. X#include "config/ttyopts.h"
  1157. X#include "ptysigler.h"
  1158. extern void doconnect(); /* XXX: gaargh */
  1159. extern void ckobey();
  1160. X
  1161. X#define verbose 0, /*XXX*/
  1162. X
  1163. static int firsttime;
  1164. X
  1165. static char resp6[6];
  1166. static char *sremote;
  1167. static int sremotelen;
  1168. X
  1169. static int sflagttymodes; /* must be 0 if !sflagreading */
  1170. static struct ttymodes stmotty; /* only valid if sflagttymodes */
  1171. static struct ttymodes stmottyzero; /* only valid if sflagttymodes */
  1172. static int sflagreading;
  1173. static int sflagjobctrl;
  1174. static int fdmaster = -1;
  1175. static int fdus2master = -1;
  1176. X
  1177. static int fdtty = -1;
  1178. X
  1179. static int suid = -1;
  1180. static char recoext[2]; /* if null, then unset */
  1181. X
  1182. void lastmoment(n)
  1183. int n;
  1184. X{
  1185. X /* clean up and die! */
  1186. X if (sflagttymodes)
  1187. X   tty_setmodes(fdtty,&stmotty);
  1188. X}
  1189. X
  1190. void stop(sig)
  1191. int sig;
  1192. X{
  1193. X sigdfl(sig);
  1194. X}
  1195. X
  1196. void byebye(sig)
  1197. int sig;
  1198. X{
  1199. X lastmoment(0);
  1200. X sigdfl(sig);
  1201. X die(DIE_IMPOSSIBLE);
  1202. X}
  1203. X
  1204. void obey(n)
  1205. int n;
  1206. X{
  1207. X int r;
  1208. X char c[4];
  1209. X
  1210. X verbose("sigler entering obey");
  1211. X r = bread(fdmaster,c,4);
  1212. X verbose("sigler obeyread %d %c %d %d %d",r,c[0],c[1],c[2],c[3]);
  1213. X
  1214. X if (r <= 0)
  1215. X   c[0] = 'd'; /* kludge alert! */
  1216. X
  1217. X switch(c[0])
  1218. X  {
  1219. X   case 'z': /* slave stopped, c[1] is stop signal */
  1220. X     if (sflagjobctrl)
  1221. X      {
  1222. X       /* XXX: error checks! */
  1223. X       if (sflagttymodes)
  1224. X         tty_setmodes(fdtty,&stmotty);
  1225. X       sigdfl(c[1]); /* my, we are so trusting... */
  1226. X       if (sflagreading)
  1227. X     tty_forcefg(fdtty);
  1228. X       if (sflagttymodes)
  1229. X     tty_setmodes(fdtty,&stmottyzero);
  1230. X       bwrite(fdus2master,"C",1); /* XXX: error checks? */
  1231. X      }
  1232. X     break;
  1233. X   case 'k': /* sesskill */
  1234. X   case 'd': /* disconnect */
  1235. X   case 'e': /* slave exited, c[1] is exit status */
  1236. X   case 's': /* slave terminated by signal, c[1] is signum, c[2] is coredump */
  1237. X     if (recoext[0])
  1238. X       doconnect();
  1239. X     else
  1240. X      {
  1241. X       close(fdmaster); fdmaster = -1;
  1242. X       close(fdus2master); fdus2master = -1;
  1243. X       ckobey();
  1244. X       lastmoment(0);
  1245. X      }
  1246. X     break;
  1247. X   case 'r': /* recoext, c[1] and c[2] */
  1248. X     recoext[0] = c[1];
  1249. X     recoext[1] = c[2];
  1250. X     break;
  1251. X  }
  1252. X}
  1253. static ss_sig *sigobey = 0;
  1254. void ckobey()
  1255. X{
  1256. X if (!sigobey && (fdmaster != -1))
  1257. X  { ss_schedwait(sigobey = ss_sigread(fdmaster),obey,0,1); return; }
  1258. X if (sigobey && (fdmaster == -1))
  1259. X  { ss_unsched(sigobey,obey,0); sigobey = 0; }
  1260. X}
  1261. X
  1262. void doconnect()
  1263. X{
  1264. X /* assumptions: */
  1265. X /* we are in the foreground if sflagreading */
  1266. X /* fd 0 is input, fd 1 is output, fd 2 is error---all open */
  1267. X /* tty is in mode smottyzero if sflagttymodes */
  1268. X
  1269. X int fdcomm;
  1270. X int pi[2];
  1271. X int sp[2];
  1272. X
  1273. X if (!firsttime)
  1274. X  {
  1275. X   char buf[50]; char *t; t = buf;
  1276. X   t += fmt_strncpy(t,"reconnecting to ",0);
  1277. X   *t++ = recoext[0]; *t++ = recoext[1]; *t = 0;
  1278. X   warn("info",buf);
  1279. X  }
  1280. X verbose("sigler entering doconnect");
  1281. X if (fdmaster != -1) { close(fdmaster); fdmaster = -1; }
  1282. X if (fdus2master != -1) { close(fdus2master); fdus2master = -1; }
  1283. X
  1284. X if ((pipe(pi) == -1) || (pipe(sp) == -1))
  1285. X  {
  1286. X   lastmoment(0);
  1287. X   if (firsttime) /* hope not */
  1288. X     warn("fatal","signaller cannot create internal pipe; master may still be running; use sesslist to check, then try sesskill or reconnect later");
  1289. X   else
  1290. X     warn("fatal","signaller cannot create internal pipe");
  1291. X   die(DIE_IMPOSSIBLE);
  1292. X  }
  1293. X
  1294. X fdcomm = comm_write(recoext,suid);
  1295. X if (fdcomm == -1) /* hope this isn't the first connect */
  1296. X  {
  1297. X   lastmoment(0);
  1298. X   if (firsttime)
  1299. X     warn("fatal","signaller cannot connect; master may still be running; use sesslist to check, then try sesskill or reconnect later");
  1300. X   else
  1301. X    {
  1302. X     char buf[100]; char *t; t = buf;
  1303. X     /* this is a typical case so we print a nice error message */
  1304. X     t += fmt_strncpy(t,"signaller cannot reconnect to session ",0);
  1305. X     t += fmt_strncpy(t,recoext,2);
  1306. X     t += fmt_strncpy(t,"; does it exist?",0);
  1307. X     *t = 0;
  1308. X     warn("fatal",buf);
  1309. X    }
  1310. X   die(DIE_EXIST);
  1311. X  }
  1312. X verbose("sigler opened connection");
  1313. X if (
  1314. X     (bwrite(fdcomm,"r",1) != 1)
  1315. X   ||(bread(fdcomm,resp6,6) != 6)
  1316. X   ||(!respeq(resp6,"shrtng"))
  1317. X   ||(comm_putfd(fdcomm,sp[1]) == -1)
  1318. X   ||(comm_putfd(fdcomm,pi[0]) == -1)
  1319. X   ||(comm_putfd(fdcomm,0) == -1)
  1320. X   ||(comm_putfd(fdcomm,1) == -1)
  1321. X   ||(bwrite(fdcomm,(char *) &sflagreading,sizeof(int)) != sizeof(int))
  1322. X   ||(bwrite(fdcomm,(char *) &sremotelen,sizeof(int)) != sizeof(int))
  1323. X   ||(bwrite(fdcomm,(char *) sremote,sremotelen) != sremotelen)
  1324. X   ||(bread(fdcomm,resp6,6) != 6)
  1325. X   ||(!respeq(resp6,"phew! "))
  1326. X    )
  1327. X  {
  1328. X   lastmoment(0);
  1329. X   if (firsttime)
  1330. X     warn("fatal","signaller having trouble; master may still be running; use sesslist to check, then try sesskill or reconnect later");
  1331. X   else
  1332. X     if (respeq(resp6,"no-go!")) /* master's already connected */
  1333. X      {
  1334. X       warn("fatal","session already connected somewhere else");
  1335. X       die(DIE_ELSE);
  1336. X      }
  1337. X     else
  1338. X       warn("fatal","signaller having trouble");
  1339. X   die(DIE_COMM);
  1340. X  }
  1341. X close(sp[1]); fdmaster = sp[0];
  1342. X close(pi[0]); fdus2master = pi[1];
  1343. X
  1344. X#ifdef TTY_WINDOWS
  1345. X /* Obviously there's a race here between the master reconnecting */
  1346. X /* and us telling the pty to change sizes. But will the slave */
  1347. X /* ever care? I'm not sure... */
  1348. X kill(getpid(),SIGWINCH); /* oh, what a royal kludge */
  1349. X#endif
  1350. X
  1351. X close(fdcomm);
  1352. X
  1353. X verbose("sigler successful doconnect");
  1354. X if (!firsttime)
  1355. X  {
  1356. X   char buf[50]; char *t; t = buf;
  1357. X   t += fmt_strncpy(t,"successfully connected to ",0);
  1358. X   *t++ = recoext[0]; *t++ = recoext[1]; *t = 0;
  1359. X   warn("info",buf);
  1360. X  }
  1361. X recoext[0] = recoext[1] = 0;
  1362. X firsttime = 0;
  1363. X
  1364. X ckobey();
  1365. X}
  1366. X
  1367. static void sigchld(n)
  1368. int n;
  1369. X{
  1370. X int w;
  1371. X
  1372. X while (wait3(&w,WNOHANG | WUNTRACED,(struct rusage *) 0) > 0)
  1373. X   ; /* [yawn] */
  1374. X}
  1375. X
  1376. static void sigwinch(n)
  1377. int n;
  1378. X{
  1379. X#ifdef TTY_WINDOWS
  1380. X struct ttywin twi;
  1381. X struct ttymodes tmo;
  1382. X
  1383. X if (tty_getmodes(fdtty,&tmo) == 0)
  1384. X  {
  1385. X   tty_modes2win(&tmo,&twi);
  1386. X   bwrite(fdus2master,"W",1);
  1387. X   bwrite(fdus2master,(char *) &twi,sizeof(twi));
  1388. X   /* XXX: error checks? */
  1389. X  }
  1390. X#endif
  1391. X ;
  1392. X}
  1393. X
  1394. X/*
  1395. Signal handling:
  1396. X
  1397. TTIN, TTOU: will never happen, as we don't do I/O; default if they do happen
  1398. PIPE: ditto; default if it does happen
  1399. HUP, INT, QUIT: default. we'll die, master will see socket close.
  1400. X  XXX: There's a race here to reconnect to the master initially...
  1401. TSTP: default. master won't see it.
  1402. X
  1403. CHLD: could be master, or a child from before we were execed; ignore both
  1404. X  XXX: There's a race upon exiting to avoid making zombies...
  1405. WINCH, and after every manual continue: tell master to winch
  1406. X
  1407. when fdmaster != -1 and it's readable: do obey(). This keeps us going.
  1408. A side effect of this strategy is that if we ever rest for a moment
  1409. without a master to obey, we die. How poetic. Should this be called the
  1410. slave process?
  1411. X*/
  1412. X
  1413. void sigler(ext,uid,pid,flagttymodes,tmotty,tmottyzero,flagreading,flagjobctrl,remote)
  1414. char *ext;
  1415. int uid;
  1416. int pid; /* process id of master---not that we care */
  1417. int flagttymodes;
  1418. struct ttymodes *tmotty;
  1419. struct ttymodes *tmottyzero;
  1420. int flagreading;
  1421. int flagjobctrl;
  1422. char *remote;
  1423. X{
  1424. X /* TTOU, TTIN, PIPE are already SIG_DFL */
  1425. X ss_sched(ss_signal(SIGINT),byebye,SIGINT);
  1426. X ss_sched(ss_signal(SIGHUP),byebye,SIGHUP);
  1427. X ss_sched(ss_signal(SIGTSTP),stop,SIGTSTP);
  1428. X ss_sched(ss_signal(SIGQUIT),byebye,SIGQUIT);
  1429. X ss_sched(ss_signal(SIGCHLD),sigchld,0);
  1430. X#ifdef TTY_WINDOWS
  1431. X ss_sched(ss_signal(SIGWINCH),sigwinch,0);
  1432. X#endif
  1433. X
  1434. X suid = uid;
  1435. X
  1436. X sremote = remote;
  1437. X sremotelen = strlen(remote) + 1;
  1438. X if (sremotelen > SESSCONNLOG_REMOTELEN)
  1439. X   sremotelen = SESSCONNLOG_REMOTELEN;
  1440. X sflagttymodes = flagttymodes;
  1441. X sflagreading = flagreading;
  1442. X sflagjobctrl = flagjobctrl;
  1443. X tty_copymodes(&stmotty,tmotty);
  1444. X tty_copymodes(&stmottyzero,tmottyzero);
  1445. X
  1446. X if (flagttymodes)
  1447. X   fdtty = tty_getctrl();
  1448. X   /* XXX: what if it's -1? is this even remotely possible? */
  1449. X
  1450. X firsttime = 1;
  1451. X
  1452. X recoext[0] = ext[0]; recoext[1] = ext[1];
  1453. X doconnect();
  1454. X}
  1455. END_OF_FILE
  1456. if test 7958 -ne `wc -c <'ptysigler.c'`; then
  1457.     echo shar: \"'ptysigler.c'\" unpacked with wrong size!
  1458. fi
  1459. # end of 'ptysigler.c'
  1460. fi
  1461. if test -f 'timer.c' -a "${1}" != "-c" ; then 
  1462.   echo shar: Will not clobber existing file \"'timer.c'\"
  1463. else
  1464. echo shar: Extracting \"'timer.c'\" \(7794 characters\)
  1465. sed "s/^X//" >'timer.c' <<'END_OF_FILE'
  1466. X/* timer.c, timer.h: timer libraries
  1467. Daniel J. Bernstein, brnstnd@nyu.edu.
  1468. Depends on sigsched.h, ralloc.h, sod.h.
  1469. Requires BSD (interval timers, PROF and VTALRM signals, gettimeofday, etc.).
  1470. X7/27/91: Baseline. timer 1.0, public domain.
  1471. No known patent problems.
  1472. X
  1473. All signals defined here are thread-lowered signals.
  1474. X
  1475. Note that we use timer_clock instead of struct timeval since
  1476. timer_clock has at least a prayer of being portable.
  1477. This implementation, however, is BSD-only.
  1478. X*/
  1479. X
  1480. X#include <sys/types.h>
  1481. X#include <sys/time.h>
  1482. X#include <sys/times.h>
  1483. X#include <signal.h>
  1484. X#include "sigsched.h"
  1485. X#include "sod.h"
  1486. X#include "timer.h"
  1487. X#include "ralloc.h"
  1488. X#ifndef HZ
  1489. X#define HZ 60 /*XXX*/
  1490. X#endif
  1491. X
  1492. int timer_now(t,result)
  1493. timer_type t;
  1494. timer_clock *result;
  1495. X{
  1496. X struct tms tms;
  1497. X struct timeval tv;
  1498. X
  1499. X switch(t)
  1500. X  {
  1501. X   case TIMER_REAL:
  1502. X     if (gettimeofday(&tv,(struct timezone *) 0) == -1)
  1503. X       return -1;
  1504. X     result->sec = tv.tv_sec;
  1505. X     result->usec = tv.tv_usec;
  1506. X     break;
  1507. X   case TIMER_VIRTUAL:
  1508. X     times(&tms);
  1509. X     result->sec = tms.tms_utime / HZ;
  1510. X     result->usec = ((tms.tms_utime % HZ) * 1000000) / HZ;
  1511. X     break;
  1512. X   case TIMER_PROF:
  1513. X     times(&tms);
  1514. X     result->sec = (tms.tms_utime + tms.tms_stime) / HZ;
  1515. X     result->usec = (((tms.tms_utime + tms.tms_stime) % HZ) * 1000000) / HZ;
  1516. X     break;
  1517. X   default:
  1518. X     return -1;
  1519. X  }
  1520. X return 0;
  1521. X}
  1522. X
  1523. void timer_sum(one,two,result)
  1524. timer_clock *one;
  1525. timer_clock *two;
  1526. timer_clock *result;
  1527. X{
  1528. X result->sec = one->sec + two->sec;
  1529. X if ((result->usec = one->usec + two->usec) >= 1000000)
  1530. X  {
  1531. X   result->sec += 1;
  1532. X   result->usec -= 1000000;
  1533. X  }
  1534. X}
  1535. X
  1536. int timer_diff(one,two,result)
  1537. timer_clock *one;
  1538. timer_clock *two;
  1539. timer_clock *result;
  1540. X{
  1541. X if (one->sec > two->sec)
  1542. X  {
  1543. X   result->sec = one->sec - two->sec;
  1544. X   if (one->usec >= two->usec)
  1545. X     result->usec = one->usec - two->usec;
  1546. X   else
  1547. X    { --result->sec; result->usec = 1000000 - (two->usec - one->usec); }
  1548. X   return 1;
  1549. X  }
  1550. X if (one->sec < two->sec)
  1551. X  {
  1552. X   result->sec = two->sec - one->sec;
  1553. X   if (two->usec >= one->usec)
  1554. X     result->usec = two->usec - one->usec;
  1555. X   else
  1556. X    { --result->sec; result->usec = 1000000 - (one->usec - two->usec); }
  1557. X   return -1;
  1558. X  }
  1559. X if (one->usec > two->usec)
  1560. X  {
  1561. X   result->sec = 0;
  1562. X   result->usec = one->usec - two->usec;
  1563. X   return 1;
  1564. X  }
  1565. X if (one->usec < two->usec)
  1566. X  {
  1567. X   result->sec = 0;
  1568. X   result->usec = two->usec - one->usec;
  1569. X   return 1;
  1570. X  }
  1571. X result->sec = 0;
  1572. X result->usec = 0;
  1573. X return 0;
  1574. X}
  1575. X
  1576. X/* Basic idea: For each kind of timer, keep a list of all scheduled */
  1577. X/* events. Set the interval timers to go off at the first events. */
  1578. X
  1579. struct kaboom { timer_clock when; ss_thread *t; int flagi; ss_id i; ss_idptr p; int wait; } ;
  1580. X
  1581. SODdecl(kaboomlist,struct kaboom);
  1582. X
  1583. X/* XXX: should use priority queues here */
  1584. X
  1585. static int numwait = 0;
  1586. X
  1587. X/* XXX: all these will have to change if TIMER_NUM changes */
  1588. static kaboomlist thead[TIMER_NUM] = { 0, 0, 0 };
  1589. static int tgoing[TIMER_NUM] = { 0, 0, 0 };
  1590. static timer_clock twhen[TIMER_NUM];
  1591. static int t2sig[TIMER_NUM] = { SIGALRM, SIGVTALRM, SIGPROF };
  1592. static int t2it[TIMER_NUM] = { ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF };
  1593. X
  1594. static void kaboomcleanup()
  1595. X{
  1596. X kaboomlist newhead;
  1597. X kaboomlist k;
  1598. X timer_type i;
  1599. X
  1600. X for (i = 0;i < TIMER_NUM;++i)
  1601. X  {
  1602. X   newhead = 0;
  1603. X   while (thead[i])
  1604. X    {
  1605. X     SODpop(thead[i],k);
  1606. X     if (SODdata(k).t) SODpush(newhead,k); else SODfree(k,rfree);
  1607. X    }
  1608. X   thead[i] = newhead;
  1609. X  }
  1610. X}
  1611. X
  1612. static void kaboom(t)
  1613. timer_type t;
  1614. X{
  1615. X kaboomlist k;
  1616. X timer_clock dummy;
  1617. X ss_thread *thread;
  1618. X
  1619. X ss_unsched(ss_signal(t2sig[t]),kaboom,t);
  1620. X tgoing[t] = 0; /* timer's out */
  1621. X for (k = thead[t];k;k = SODnext(k))
  1622. X   if (timer_diff(&(SODdata(k).when),twhen + t,&dummy) <= 0)
  1623. X    {
  1624. X     thread = SODdata(k).t; SODdata(k).t = 0;
  1625. X     if (SODdata(k).wait) --numwait; SODdata(k).wait = 0;
  1626. X     if (thread)
  1627. X       if (SODdata(k).flagi)
  1628. X     thread(SODdata(k).i);
  1629. X       else
  1630. X     thread(SODdata(k).p);
  1631. X     /* k may now be invalid. alternative: ss_schedonce(ss_asap(),...) */
  1632. X     break; /* important! */
  1633. X    }
  1634. X kaboomcleanup();
  1635. X if (kaboomresched(t) == -1)
  1636. X   ; /* XXXXXX: uh-oh */
  1637. X}
  1638. X
  1639. static int set_it(t,when)
  1640. timer_type t;
  1641. timer_clock *when;
  1642. X{
  1643. X struct itimerval it;
  1644. X timer_clock now;
  1645. X timer_clock diff;
  1646. X
  1647. X if (timer_now(t,&now) == -1)
  1648. X   return -1;
  1649. X if (timer_diff(when,&now,&diff) <= 0)
  1650. X  {
  1651. X   diff.sec = 0;
  1652. X   diff.usec = 1;
  1653. X  }
  1654. X it.it_value.tv_sec = diff.sec; it.it_interval.tv_sec = 0;
  1655. X it.it_value.tv_usec = diff.usec; it.it_interval.tv_usec = 0;
  1656. X if (setitimer(t2it[t],&it,(struct itimerval *) 0) == -1)
  1657. X   return -1;
  1658. X return 0;
  1659. X}
  1660. X
  1661. static int kaboomresched(t) /* XXX: implicit-static */
  1662. timer_type t;
  1663. X{
  1664. X timer_clock dummy;
  1665. X kaboomlist k;
  1666. X int resched;
  1667. X
  1668. X if (thead[t])
  1669. X  {
  1670. X   resched = 0;
  1671. X   for (k = thead[t];k;k = SODnext(k))
  1672. X     if (SODdata(k).t)
  1673. X       if (!tgoing[t] || timer_diff(&SODdata(k).when,twhen + t,&dummy) < 0)
  1674. X    {
  1675. X     resched = 1;
  1676. X     twhen[t] = SODdata(k).when; /*XXX: structure copying*/
  1677. X    }
  1678. X   if (resched)
  1679. X    {
  1680. X     if (tgoing[t])
  1681. X       ss_unsched(ss_signal(t2sig[t]),kaboom,t);
  1682. X     tgoing[t] = 1;
  1683. X     if (ss_schedwait(ss_signal(t2sig[t]),kaboom,t,numwait) == -1)
  1684. X       return -1; /*XXX*/
  1685. X     if (set_it(t,twhen + t) == -1)
  1686. X       return -1; /*XXX*/
  1687. X    }
  1688. X  }
  1689. X return 0;
  1690. X}
  1691. X
  1692. static int timer_sched(x,t,flagi,i,p,wait)
  1693. ss_extern *x;
  1694. ss_thread *t;
  1695. int flagi;
  1696. ss_id i;
  1697. ss_idptr p;
  1698. int wait;
  1699. X{
  1700. X timer_sig *tsig;
  1701. X kaboomlist k;
  1702. X int resched;
  1703. X timer_clock dummy;
  1704. X
  1705. X k = SODalloc(kaboomlist,k,ralloc);
  1706. X if (!k)
  1707. X   return -1;
  1708. X tsig = (timer_sig *) x->u.c;
  1709. X SODdata(k).when.sec = tsig->when.sec;
  1710. X SODdata(k).when.usec = tsig->when.usec;
  1711. X SODdata(k).t = t;
  1712. X SODdata(k).flagi = flagi;
  1713. X SODdata(k).i = i;
  1714. X SODdata(k).p = p;
  1715. X SODdata(k).wait = wait;
  1716. X
  1717. X resched = 0;
  1718. X if (wait)
  1719. X   if (!numwait++)
  1720. X     resched = 1;
  1721. X SODpush(thead[tsig->t],k);
  1722. X if (!tgoing[tsig->t] || (timer_diff(&(SODdata(k).when),twhen + tsig->t,&dummy) < 0))
  1723. X   resched |= 2;
  1724. X if (resched)
  1725. X  {
  1726. X   if (tgoing[tsig->t])
  1727. X     ss_unsched(ss_signal(t2sig[tsig->t]),kaboom,tsig->t);
  1728. X   if (ss_schedwait(ss_signal(t2sig[tsig->t]),kaboom,tsig->t,numwait) == -1)
  1729. X     return -1;
  1730. X   tgoing[tsig->t] = 1;
  1731. X   if (resched & 2) twhen[tsig->t] = SODdata(k).when; /*XXX: struct copying*/
  1732. X   if (set_it(tsig->t,twhen + tsig->t) == -1)
  1733. X     return -1;
  1734. X  }
  1735. X return 0;
  1736. X}
  1737. X
  1738. static int timer_unsched(x,t,flagi,i,p)
  1739. ss_extern *x;
  1740. ss_thread *t;
  1741. int flagi;
  1742. ss_id i;
  1743. ss_idptr p;
  1744. X{
  1745. X timer_sig *tsig;
  1746. X kaboomlist k;
  1747. X struct kaboom *sk;
  1748. X
  1749. X tsig = (timer_sig *) x->u.c;
  1750. X for (k = thead[tsig->t];k;k = SODnext(k))
  1751. X  {
  1752. X   sk = &(SODdata(k));
  1753. X   if ((sk->t == t) && (sk->flagi == flagi) && (sk->i == i) && (sk->p == p))
  1754. X     if ((sk->when.usec == tsig->when.usec) && (sk->when.sec == tsig->when.usec))
  1755. X      {
  1756. X       sk->t = 0;
  1757. X       if (sk->wait)
  1758. X     --numwait;
  1759. X       sk->wait = 0;
  1760. X      }
  1761. X  }
  1762. X kaboomcleanup();
  1763. X if (kaboomresched(tsig->t) == -1)
  1764. X   return -1;
  1765. X return 0;
  1766. X}
  1767. X
  1768. void timer_setsig(tsig,t,when)
  1769. timer_sig *tsig;
  1770. timer_type t;
  1771. timer_clock *when;
  1772. X{
  1773. X tsig->x.sched = timer_sched;
  1774. X tsig->x.unsched = timer_unsched;
  1775. X tsig->x.u.c = (char *) tsig; /* my, aren't we the clever ones today */
  1776. X tsig->t = t;
  1777. X tsig->when.sec = when->sec;
  1778. X tsig->when.usec = when->usec;
  1779. X ss_externsetsig(&(tsig->sig),&(tsig->x));
  1780. X}
  1781. X
  1782. static int init = 0;
  1783. X
  1784. int timer_init()
  1785. X{
  1786. X struct itimerval it;
  1787. X it.it_value.tv_sec = 0; it.it_value.tv_usec = 0;
  1788. X it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0;
  1789. X if (init)
  1790. X   return 0;
  1791. X init = 1;
  1792. X if (ss_addsig(SIGALRM) == -1)
  1793. X   return -1;
  1794. X if (setitimer(ITIMER_REAL,&it,(struct itimerval *) 0) == -1)
  1795. X   return -1;
  1796. X if (ss_addsig(SIGPROF) == -1)
  1797. X   return -1;
  1798. X if (setitimer(ITIMER_PROF,&it,(struct itimerval *) 0) == -1)
  1799. X   return -1;
  1800. X if (ss_addsig(SIGVTALRM) == -1)
  1801. X   return -1;
  1802. X if (setitimer(ITIMER_VIRTUAL,&it,(struct itimerval *) 0) == -1)
  1803. X   return -1;
  1804. X /* we may end up receiving up to one of each signal, but that's okay */
  1805. X return 0;
  1806. X}
  1807. END_OF_FILE
  1808. if test 7794 -ne `wc -c <'timer.c'`; then
  1809.     echo shar: \"'timer.c'\" unpacked with wrong size!
  1810. fi
  1811. # end of 'timer.c'
  1812. fi
  1813. echo shar: End of archive 5 \(of 9\).
  1814. cp /dev/null ark5isdone
  1815. MISSING=""
  1816. for I in 1 2 3 4 5 6 7 8 9 ; do
  1817.     if test ! -f ark${I}isdone ; then
  1818.     MISSING="${MISSING} ${I}"
  1819.     fi
  1820. done
  1821. if test "${MISSING}" = "" ; then
  1822.     echo You have unpacked all 9 archives.
  1823.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1824. else
  1825.     echo You still need to unpack the following archives:
  1826.     echo "        " ${MISSING}
  1827. fi
  1828. ##  End of shell archive.
  1829. exit 0
  1830.